package com.hontech.libratest.conn

import com.hontech.libratest.app.ConnTask
import com.hontech.libratest.except.AckException
import com.hontech.libratest.except.ExecFailException
import com.hontech.libratest.except.RespException
import com.hontech.libratest.utils.Frame
import com.hontech.libratest.utils.SerialType
import com.hontech.libratest.utils.UInt16
import com.hontech.libratest.utils.UInt8
import kotlinx.coroutines.CompletableDeferred
import java.io.IOException
import java.lang.IllegalStateException


suspend fun send(req: Int, args: Array<SerialType>, timeout: Long): Frame {
    val future = CompletableDeferred<Frame>()
    val task = ConnExecTask(req, args, timeout, future)
    ConnTask.post(task)
    return future.await()
}

suspend fun request(req: Int, args: Array<SerialType>, timeout: Long, name: String) {
    val frame = send(req, args, timeout)
    val value = UInt8()
    frame.parse(arrayOf(value))
    if (!value.isOk()) {
        throw ExecFailException("${name}异常: ${value.errMsg()}")
    }
}

suspend fun testPush(type: Int) {
    val args = arrayOf<SerialType>(UInt8(type))
    request(Proto.TestPushMotor, args, 10 * 1000L, "测试推杆")
}

suspend fun testElevatorByRow(type: Int, row: Int = 1) {
    val args = arrayOf<SerialType>(UInt8(type), UInt8(row))
    request(Proto.TestElevatorByRow, args, 45 * 1000L, "测试升降电机")
}

suspend fun testElevatorByMs(ms: Int) {
    val args = arrayOf<SerialType>(UInt16(ms))
    request(Proto.TestElevatorByMs, args, 15 * 1000L, "测试升降电机2")
}

suspend fun setElevatorRowHigh(row: Int, ms: Int) {
    val args = arrayOf<SerialType>(UInt8(row), UInt16(ms))
    request(Proto.SetRowHigh, args, 5 * 1000L, "设置层高")
}

suspend fun getElevatorRowHigh(row: Int): Int {
    val args = arrayOf<SerialType>(UInt8(row))
    val frame = send(Proto.GetRowHigh, args, 5 * 1000L)
    val err = UInt8()
    val value = UInt16()
    frame.parse(arrayOf(err, value))
    if (!err.isOk()) {
        throw ExecFailException("查询层高异常: ${err.errMsg()}")
    }
    return value.value
}

suspend fun enableElevator() {
    val args = arrayOf<SerialType>(UInt8(0))
    request(Proto.SetUseElevator, args, 2 * 1000L, "启用升降")
}

suspend fun disableElevator() {
    val args = arrayOf<SerialType>(UInt8(1))
    request(Proto.SetUseElevator, args, 2 * 1000L, "禁用升降")
}

suspend fun getUseElevator(): Boolean {
    val frame = send(Proto.GetUseElevator, emptyArray(), 2 * 1000L)
    val value = UInt8()
    frame.parse(arrayOf(value))
    return value.value == 0
}

suspend fun setElevatorSpeed(baseFreq: Int, varRange: Int, acc: Int) {
    val args = arrayOf<SerialType>(UInt16(baseFreq), UInt16(varRange), UInt16(acc))
    request(Proto.SetElevatorSpeed, args, 2 * 1000L, "设置升降速度")
}

class ElevatorSpeed(val baseFreq: Int, val varRange: Int, val acc: Int)

suspend fun getElevatorSpeed(): ElevatorSpeed {
    val frame = send(Proto.GetElevatorSpeed, emptyArray(), 2 * 1000L)
    val baseFreq = UInt16()
    val varRange = UInt16()
    val acc = UInt16()
    frame.parse(arrayOf(baseFreq, varRange, acc))
    return ElevatorSpeed(baseFreq.value, varRange.value, acc.value)
}

suspend fun pickDoorOpen() {
    request(Proto.TestPickDoor, arrayOf(UInt8(0)), 5 * 1000L, "打开取物门")
}

suspend fun pickDoorClose() {
    request(Proto.TestPickDoor, arrayOf(UInt8(1)), 5 * 1000L, "打开取物门")
}

suspend fun getCargoType(row: Int): Int {
    val frame = send(Proto.GetCargoType, arrayOf(UInt8(row)), 2 * 1000L)
    val err = UInt8()
    val value = UInt8()
    frame.parse(arrayOf(err, value))
    if (!err.isOk()) {
        throw ExecFailException("查询货道类型异常: ${err.errMsg()}")
    }
    return value.value
}

suspend fun setCargoType(row: Int, type: Int) {
    request(Proto.SetCargoType, arrayOf(UInt8(row), UInt8(type)), 2 * 1000L, "设置货道类型")
}

suspend fun doorOpen() {
    request(Proto.OpenDoor, emptyArray(), 2 * 1000L, "大门打开")
}

suspend fun beepOn() {
    request(Proto.TestBeep, arrayOf(UInt8(0)), 2 * 1000L, "蜂鸣器打开")
}

suspend fun beepOff() {
    request(Proto.TestBeep, arrayOf(UInt8(1)), 2 * 1000L, "蜂鸣器关闭")
}

private suspend fun preProc() {
    request(Proto.PreProce, emptyArray(), 2 * 1000L, "出货预处理")
}

private suspend fun deliveryImpl(row: Int, col: Int, size: Int, timeout: Int) {
    request(
        Proto.Delivery,
        arrayOf(UInt8(row), UInt8(col), UInt8(size), UInt8(timeout)),
        (timeout * 60 + 10) * 1000L,
        "出货"
    )
}

private suspend fun preProc3() {
    for (i in 0 until 3) {
        try {
            preProc()
            return
        } catch (e: AckException) {
            e.printStackTrace()
        } catch (e: RespException) {
            e.printStackTrace()
        }
    }
    throw IOException("出货预处理失败")
}

suspend fun delivery(row: Int, col: Int, size: Int, timeout: Int) {

    preProc3()

    for (i in 0 until 3) {

        try {
            deliveryImpl(row, col, size, timeout)
            return
        } catch (e: AckException) {
            e.printStackTrace()
        }
    }

    throw IOException("发送出货命令失败")
}

suspend fun reset() {
    request(Proto.Reset, emptyArray(), 30 * 1000L, "设备复位")
}

suspend fun runOta(data: ByteArray, fn: (Int) -> Unit) {
    val future = CompletableDeferred<Unit>()
    val task = OtaTask(data, fn, future)
    ConnTask.post(task)
    future.await()
}


suspend fun readRkc(reg: Int): Int {
    val frame = send(Proto.ReadRkc, arrayOf(UInt16(reg)), 2 * 1000L)
    val err = UInt8()
    val value = UInt16()
    frame.parse(arrayOf(err, value))
    if (!err.isOk()) {
        throw ExecFailException("读取温控器失败: ${err.errMsg()}")
    }
    return value.value
}

suspend fun writeRkc(reg: Int, value: Int) {
    request(Proto.WriteRkc, arrayOf(UInt16(reg), UInt16((value))), 2 * 1000L, "写入温控器失败")
}

suspend fun cargoTest(row: Int, col: Int, size: Int) {
    val args = arrayOf<SerialType>(UInt8(row), UInt8(col), UInt8(size))
    request(Proto.CargoTest, args, 10 * 1000L, "货道测试")
}

suspend fun cargoCheck(row: Int, col: Int) {
    request(Proto.CargoCheck, arrayOf(UInt8(row), UInt8(col)), 10 * 1000L, "货道检测")
}

suspend fun cargoReset(row: Int, col: Int) {
    request(Proto.CargoReset, arrayOf(UInt8(row), UInt8(col)), 10 * 1000L, "货道复位")
}

suspend fun setRasterPick(value: Int) {
    request(Proto.SetRasterPick, arrayOf(UInt8(value)), 2 * 1000L, "设置光栅取货")
}

suspend fun getRasterPick(): Int {
    val result = send(Proto.GetRasterPick, emptyArray(), 2 * 1000L)
    val value = UInt8()
    result.parse(arrayOf(value))
    return value.value
}

suspend fun setClaspSize(row: Int, size: Int) {
    request(Proto.SetClaspSize, arrayOf(UInt8(row), UInt8(size)), 2 * 1000L, "设置挂钩步距")
}

suspend fun getClaspSize(row: Int): Int {
    val result = send(Proto.GetClaspSize, arrayOf(UInt8(row)), 2 * 1000L)
    val err = UInt8()
    val value = UInt8()
    result.parse(arrayOf(err, value))
    if (!err.isOk()) {
        throw IllegalStateException("获取步距失败:${err.errMsg()}")
    }
    return value.value
}

suspend fun ledOpen() {
    request(Proto.SET_LED, arrayOf(UInt8(0)), 2 * 1000L, "LED打开")
}

suspend fun ledClose() {
    request(Proto.SET_LED, arrayOf(UInt8(1)), 2 * 1000L, "LED关闭")
}


